function _(id){
	return document.getElementById(id);
}
/**
 * @description 跨浏览器getElementsByClassName实现
 * @param {String} searchClass  '{tag}.{className}'
 * @param {HTMLElement} node 搜索的的目标父节点
 * @returns {Array(HTMLElement)} 数组
 */
_._ = function (searchClass, node) {
	var result = [], sc = searchClass.split('.'), tag;
	if(sc.length ===2){
		searchClass = sc[1];
		tag = sc[0]||'*';
	}else{
		return null;
	}
	if(document.getElementsByClassName){
		var nodes =  (node || document).getElementsByClassName(searchClass);
		for(var i=0 ;node = nodes[i++];){
			if(tag !== "*" && node.tagName === tag.toUpperCase()){
				result.push(node);
			}else{
				result.push(node);
			}
		}
    }else{
        if ( node == null )
                node = document;
        var els = node.getElementsByTagName(tag);
        var elsLen = els.length;
        var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
        for (i = 0, j = 0; i < elsLen; i++) {
			if ( pattern.test(els[i].className) ) {
				result[j] = els[i];
				j++;
			}
        }
    }
	return result;
};
/**
 * @description 判断是否是IE，如果是返回具体版本号
 * @returns IE的版本号，W3C系列返回undefined
 * */
_.isIE = (function(){
    var undef, v = 3,
        div = document.createElement('div'),
        all = div.getElementsByTagName('i');
    while (
        div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
        all[0]
    );
    return v > 4 ? v : undef;
}());
/**
 * @description 判断是否是数组
 */
_.isArray = function(o){
	return Object.prototype.toString.call(o) === '[object Array]';
};
/**
 * @description 去除字符串的首尾空白
 */
_.trim = function(text){
	if(String.prototype.trim){
		return text == null ?"" :String.prototype.trim.call(text);
	}else{
		return text == null ?"" :text.toString().replace( /^\s+/, "" ).replace(  /\s+$/, "" );
	}
};
/**
 * @description 序列化对象为查询字符串
 * @param {Object} obj 待序列化的对象，如{a:1,b:2,c:0,d:false,e:null}，0和false返回对应的字符串，null和undefined返回空
 * @returns {String} 形如'a=1&b=2&c=0&d=false&e='的字符串
 */
_.serialize = function(obj){
	if(typeof obj != 'object') return '';
	var ret = [];
	for(var key in obj){
		ret.push(key + '=' + (obj[key] || (_.contains([0,false],obj[key])?String(obj[key]):'')));
	}
	return ret.join('&');
};
_.contains = function(array,item){
	return _.indexOf(array,item) != -1;
};
_.indexOf = function(array,item){
	var retIdx = -1;
	if(!array || !array.length) return retIdx;
	for(var index in array){
		if(item === array[index]){
			retIdx = index;
			break;
		}
	}
	return parseInt(retIdx);
};
/**
 * @description from jquery.cookie.js
 * @param {Object} options {expires,path,domain,secure}
 */
_.cookie = function(name,value,options){
	 if (typeof value != 'undefined') { // name and value given, set cookie
	    options = options || {};
	    if (value === null) {
	        value = '';
	        options.expires = -1;
	    }
	    var expires = '';
	    if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
	        var date;
	        if (typeof options.expires == 'number') {
	            date = new Date();
	            date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
	        } else {
	            date = options.expires;
	        }
	        expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
	    }
	    var path = options.path ? '; path=' + options.path : '';
	    var domain = options.domain ? '; domain=' + options.domain : '';
	    var secure = options.secure ? '; secure' : '';
	    document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
	} else { // only name given, get cookie
	    var cookieValue = '';
	    if (document.cookie && document.cookie != '') {
	        var cookies = document.cookie.split(';');
	        for (var i = 0; i < cookies.length; i++) {
	            var cookie = _.trim(cookies[i]);
	            // Does this cookie string begin with the name we want?
	            if (cookie.substring(0, name.length + 1) == (name + '=')) {
	                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
	                break;
	            }
	        }
	    }
	    return cookieValue;
	}
};
_.url = function(url){
	var a =  document.createElement('a');
	a.href = url;
	return {
		source: url,
		protocol: a.protocol.replace(':',''),
		host: a.hostname,
		port: a.port,
		query: a.search,
		params: (function(){
			var ret = {},
			seg = a.search.replace(/^\?/,'').split('&'),
			len = seg.length, i = 0, s;
			for (;i<len;i++) {
				if (!seg[i]) { continue; }
				s = seg[i].split('=');
				ret[s[0]] = s[1];
			}
			return ret;
		})(),
		file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
		hash: a.hash.replace('#',''),
		path: a.pathname.replace(/^([^\/])/,'/$1'),
		relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
		segments: a.pathname.replace(/^\//,'').split('/')
	};
};
/**
 * @description 轻量级AOP实现，目前用于/user/js/map.js中的请求超时次数统计；更完善的版本参见：http://www.cnblogs.com/riceball/archive/2007/09/02/jsInject.html
 * @param {Object} args {name:String,context:Object,start:Function,end:Function} context为执行环境，默认为window，name为函数名称，start为前置操作，end为后置操作
 * @usage _.inject({name:'alert',context:window,start:function(){console.log('start')},end:function(){console.log('end')}});
 */
_.inject = function(args){
	var origin = args.name,
		context = args.context || window,
		start = args.start || (function(){}),
		end = args.end || (function(){}),
		temp = context[origin];
	if(typeof temp != 'function')
		throw Error(origin + ' is not a function in the given context.');
	if(typeof start != 'function' || typeof end != 'function' )
		throw Error('start or end should be a function.');
	context[origin] = function(){
		start.apply(this,arguments);
		var ret = temp.apply(this,arguments);
		end.apply(this,arguments);
		return ret;
	};
};
/**
 * @description 批量设置样式表
 * @param {HTMLElement} el HTML节点
 * @param {String} cssText style样式
 * @param {Boolean} overWrite 是否重写现有节点style属性，默认为false
 */
_.css = function (el,cssText,overWrite){
	if(el&&el.style){
		if(overWrite)
			el.style.cssText = cssText;
		else
			el.style.cssText += ';'+ cssText;
	}
};
/**
 * @description 动态插入样式表到head
 * @param {String} cssText css
 * @param {String} id style节点id
 */
_.css.append = function(cssText,id){
	var css = document.createElement('style') ,head = document.head || document.getElementsByTagName("head")[0];
	if(id){
		css.setAttribute('id',id);
	}
    css.setAttribute('type', 'text/css');
	//IE
    if(css.styleSheet) { 
        css.styleSheet.cssText = cssText;
    } else { 
        css.appendChild(document.createTextNode(cssText));
    }
    head.appendChild(css);
};
/**
 * @description 切换样式表，移除所有包含此样式的元素；给当前节点加上此样式
 * @param {HTMLElement} node 当前节点
 * @param {String} cls 样式表
 * @param {HTMLElement} context 移除样式表元素的父节点
 */
_.css.toggle = function(node,cls,context){
	context = context || document.body;
	var clsArr = cls.split('.');
	if(clsArr.length !=2)return;
	var className = clsArr[1];
	var olds = _._(cls,context), len = olds.length, i = 0;
	for(;i<len;i++){
		olds[i].className = olds[i].className.replace(className,'');
	}
	var current = node.className;
	if(current.indexOf(className)== -1){
		node.className = current+ ' ' + className;
	}
};
_.dom = function(){};
/**
 * @description 调用此方法来保证插入节点后再设置节点的innerHTML，避免在IE中的内存泄漏
 * @param {HTMLElement} parent 父节点
 * @param {String} tagName 标签名称
 * @returns {HTMLElement} 新增的节点
 */
_.dom.append = function(parent,tagName){
	var node = document.createElement(tagName);
	parent.appendChild(node);
	return node;
};
/**
 * @description 采用ext的处理方式，也可以对删除的元素使用outerHTML=''，但是此方法不通用，某些元素的outerHTML属性只读
 * @param {HTMLElement} n 要删除的HTML节点
 */
_.dom.remove = function(n){
	var d;
	if(_.isIE){
        if(n && n.tagName != 'BODY'){
            d = d || document.createElement('div');
            d.appendChild(n);
            d.innerHTML = '';
        }
	}else{
	    if(n && n.parentNode && n.tagName != 'BODY'){
	        n.parentNode.removeChild(n);
	    }
	}
};
_.ajax = function(){};
//存储超时或异常的JSONP请求
_.ajax.TIMEOUT_REQUEST = [];
//单次请求超时设定，不能设置的太小，有些客户的网络较差
_.ajax.TIMEOUT = 5000;
(function(){
	//为IE添加重用的动态script标签，标准浏览器无法重用script标签;重用的script标签可能被占用，也可能请求时发送网络错误，因此需要添加额外的处理机制；
	if(_.isIE){
		//JSONP重用标签的id
		_.ajax.SCRIPT_ID = 'ie_script_for_jsonp';
		//重用标签是否被JSONP请求占用
		_.ajax.SCRIPT_USED = false;
		//被占用时JSONP请求等待的时间
		_.ajax.WAIT_TIME = 300;
		//上次JSONP请求的时间
		_.ajax.LAST_USED_TIME = 0;
		var script = document.createElement('script'), head = document.head || document.getElementsByTagName('head')[0];
		script.setAttribute('id',_.ajax.SCRIPT_ID);
		script.onreadystatechange = function(){
			if (this.readyState == "loaded" || this.readyState == "complete"){
				_.ajax.SCRIPT_USED = false;
			}
		};
		head.appendChild(script);
	}
})();
/**
 * @description 轮询JSONP请求调用，IE浏览器采用重用Script节点方式。IE 7 下似乎使用第二个参数还有问题，待考证。
 * @param {String} url 手动加上callback参数，自动追加了时间戳
 * @param {Object} queryStringObj 可选的附加参数
 */
_.ajax.jsonp = function(url,queryStringObj){
	if(!navigator.onLine){
		_.ajax.TIMEOUT_REQUEST.push(url);
		return;
	}
	var script, now = new Date().getTime(), 
		requestUrl = url + (url.indexOf('?')>-1?'&timestamp=':'?timestamp=') + now + (queryStringObj?('&' + _.serialize(queryStringObj)):''),
		head = document.head || document.getElementsByTagName('head')[0];
	//IE 9已经修复此script重用机制，将进入标准模式
	if(_.isIE && _.isIE < 9){
		script = document.getElementById(_.ajax.SCRIPT_ID);
		//节点被占用
		if(_.ajax.SCRIPT_USED){
			if(_.ajax.LAST_USED_TIME === 0)
				_.ajax.LAST_USED_TIME = now;
			//已经超时
			if((now - _.ajax.LAST_USED_TIME) > _.ajax.TIMEOUT){
				_.ajax.LAST_USED_TIME = now;
				if(_.ajax.TIMEOUT_REQUEST.length>=100)
					_.ajax.TIMEOUT_REQUEST.length = 0;
				_.ajax.TIMEOUT_REQUEST.push(script.src.split('&timestamp=')[0]);
				script.src = requestUrl;
			//没有超时则等待
			}else{
				setTimeout(function(){_.ajax.jsonp(url);},_.ajax.WAIT_TIME);
			}
		}else{
			_.ajax.SCRIPT_USED = true;
			_.ajax.LAST_USED_TIME = now;
			script.src = requestUrl;
		}
	}else{
		script = document.createElement('script');
		head.appendChild(script);
		script.onload =	function(){
			this.onload = null;
			this.parentNode.removeChild(this);
		};
		//opera 不支持onerror事件，不过无关紧要
        script.onerror = function(){
            _.ajax.TIMEOUT_REQUEST.push(this.src);
        };
		script.src = requestUrl;
	}
};
/**
 * @description 获取远程脚本，只适合加载一次的脚本，轮询请使用_.ajax.jsonp。
 * @param {Function} callback 加载脚本成功之后的回调函数
 */
_.ajax.getScript = function(url,callback){
	var script = document.createElement('script'), head = document.head || document.getElementsByTagName('head')[0], 
		cbType = typeof(callback);
	if(cbType=='function'){
		if(script.readyState){
			script.onreadystatechange = function(){
				if (this.readyState == "loaded" || this.readyState == "complete"){
					callback();
				}
			};
		}
		else{
			script.onload = callback;
		}
	}
	script.src = url;
	head.appendChild(script);
};
/**
 * @description 获取多个远程脚本文件，只适合加载一次。轮询请使用_.ajax.jsonp
 */
_.ajax.getScripts = function(urlArray){
	if(_.isArray(urlArray)){
		for(var index in urlArray){
			_.ajax.getScript(urlArray[index]);
		}
	}
};
/**
 * @description 网络模块
 * @property {Number} DETECT_CYCLE 切换到备用服务器后，监测主域名的周期，单位为秒，周期长度最好不要设置为10的倍数，便于调试观察
 */
_.net = { DETECT_CYCLE:55 };
/**
 * @description 为跨域请求函数注入容灾备份机制，需要将原函数的请求服务器地址改成_.net['funcname_current_server'];
 * 实现原理：使用对象存储每一次请求使用的服务器地址和请求结果，然后在每一次请求之前，判断前两次使用相同服务器的请求是否都不成功。
 * 如果都不成功表明连续两次请求失败则切换到备用服务器
 * @param {Object} entry 入口函数{context:执行环境,name:函数名称}
 * @param {Object} exit 回调函数{context:执行环境,name:函数名称}
 * @param {Object} settings 服务端配置{server:主域地址,backupServer:备选服务器地址，数组，长度为0则不启用容灾机制,serverImg:监测主域名可用的图片}
 * @param {Function} forward 动态判断是否在入口函数继续执行注入的容灾机制
 * @usage 参见/user/js/map.js
 * @dependacy {Function} _.inject
 */
_.net.backup = function(entry,exit,settings,forward){
	var server = settings.server,currentServer = entry.name + '_current_server';
	_.net[currentServer] = server;
	//没有备用地址则不启动容灾备份机制
	if(!settings.backupServer || !_.isArray(settings.backupServer) || !settings.backupServer.length) return;
	var interval = entry.name + '_interval',//切换到备用以后发起定时器key_name
		total = entry.name + '_total',//总的请求次数key_name
		status = entry.name + '_status',//维护各次请求的地址和请求结果key_name
		serverAvailable = entry.name + '_server_available';//表明当前主服务器是否可用的key_name
	//主服务器可用时立即切换，并重置对应的变量
	var resetServer = function(){
		_.net[serverAvailable] = true;
		_.net[currentServer] = server;
		clearInterval(_.net[interval]);
	};
	//筛选服务器算法，如果当前使用主服务器则切换到备用，如果已经是备用，有多个备用时则切换到下一个备用，只有一个备用是切换到主服务器
	var selectServer = function(){
		if(_.net[currentServer] == server){
			return settings.backupServer[0];
		}else{
			var len = settings.backupServer.length;
			if(len ==1)
				return server;
			var index = _.indexOf(settings.backupServer,_.net[currentServer]);
			return settings.backupServer[index+1] || settings.backupServer[0];
		}
	};
	_.net[serverAvailable] = true;
	_.net[interval] = null;
	_.net[total] = 0;
	_.net[status] = {};//格式：{1:['http://tbo.gpsoo.net/',true], 2:['http://tbo.gpsoo.net/']}
	entry.start = function(){
		if(typeof forward == 'function'){
			if(!forward())return;
		}
		_.net[total] += 1;
		var count = _.net[total];
		//存储当此请求使用的服务器地址和响应结果
		_.net[status][count-1] = [];
		if(count > 2){
			var prev = _.net[status][count-2], prevPrev = _.net[status][count-3];
			//上两次请求使用同一个服务器且都失败了，则切换服务器
			if(prev[0] == prevPrev[0] && !prev[1] && !prevPrev[1]){
				var usingServer = _.net[currentServer] == server, old = _.net[currentServer];
				if(usingServer){
					_.net[interval] = setInterval(function(){ 
						var img = new Image();
						img.onload = resetServer;
						img.src = settings.serverImg + (settings.serverImg.indexOf('?')>-1?'&':'?') + 't=' + new Date().getTime();
					}, _.net.DETECT_CYCLE*1000);
				}
				//如果只有一个备用服务器且备用不可用则立即切换到tbo
				if(!usingServer && settings.backupServer.length==1){
					resetServer();
				}else{
					_.net[currentServer] = selectServer(_.net[currentServer]);
					_.net[serverAvailable] = _.net[currentServer] == server;
				}
				if(settings.errorHandler){
					settings.errorHandler(old,_.net[currentServer]);
				}
			}else{
				//如果备用服务器一直正常，那么需要一直检测主域名是否可用，如果可用立即切换到主域名
				if(_.net[currentServer] != server){
					if(_.net[serverAvailable]){
						resetServer();
					}
				}
			}
			//删除冗余信息，保证数组长度为2
			delete _.net[status][count-3];
		}
		//记录当此请求使用的服务器地址
		_.net[status][count-1][0] = _.net[currentServer];
	};
	exit.start = function(){
		//标记本次请求成功，不成功的请求数组长度为1
		_.net[status][_.net[total]-1][1] = true;
	};
	_.inject(entry);
	_.inject(exit);
};
(function(document){
	window.goome = window.goome || {};
	var _url = location.href;
	var _host = _url.indexOf("www.coomix.net")>-1 ? "http://dev.coomix.net" : "http://www.coomix.net";
	var errorArr = [];
	var timerId = null;
	var delay = 10000;//延伸10秒提交请求
	/**
	 * @describ     错误信息上传
	 * @param msg   必需 错误信息
	 * @param url   可选 错误行
	 * @param line  可选 错误列
	 * @param limit 可选 激活上传的错误总数
	 */
	goome.$error = function(msg,url,line,limit){
		//sMsg,sUrl,sLine,limit
		var arg = arguments;
		var obj = {};
		if(typeof arg[0]==="object"){
			if(!arg[0].msg){
				throw new Error("Error due to:goome.$error parma obj.msg is need");
			}else{
				obj = arg[0];
				obj.line = obj.line || "?";
				obj.url = obj.url || "?";
			}
		}else if(typeof arg[0]==="string"){
			obj.msg = arg[0];
			obj.url = arg[1] || "?";
			obj.line = arg[2] || "?";
			obj.limit = arg[3] || undefined;
		}else{
			throw new Error("Error due to:goome.$error parma error");
		}
		errorArr.push(obj);
		var limit = obj.limit;
		if(limit){
			if(typeof limit !=="number"){
				throw new Error("Error due to:parma limit should be a number");
			}else if(errorArr.length===limit){
				upLoadError(errorArr);
			}
		}else{
			if(timerId){
				clearTimeout(timerId);
				timerId = null;
			}
			timerId = setTimeout(function(){
				upLoadError(errorArr);
			},delay);
		}
	};
	function upLoadError(arr){
		if(arr.length){
			var content = "#错误信息#\n";
			for(var i=0;i<arr.length;i++){
				content += "错误"+(i+1)+":";
				var o = arr[i];
				content += "错误信息:"+o.msg;
				content += "行:"+o.line;
				content += "文件:"+o.url+"\n";
			}
			arr.length = 0;
			if(!document.getElementById("error_upload_frame")){
				var oFrame = document.createElement("iframe");
				oFrame.id = "error_upload_frame";
				oFrame.src = _host + '/open/message/message_error.jsp#'+content;
				oFrame.width=0;
				oFrame.height=0;
				oFrame.frameborder=0;
				document.body.appendChild(oFrame);
			}else{
				document.getElementById("error_upload_frame").src =  _host + '/open/message/message_error.jsp?r='+(+new Date())+'#'+content;
			}
		}
	}
})(document);